/*
 *
 *  * Copyright (c) GoKeep by gokeep.org. 2022-2023. All rights reserved
 *
 */

package org.gokeep.common.dto;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.experimental.Accessors;
import org.apache.commons.io.FileUtils;
import org.gokeep.common.config.properties.KeyStoreProperties;
import org.gokeep.common.util.JsonUtil;

import java.io.File;
import java.nio.charset.Charset;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import static org.gokeep.common.config.GlobalConfig.*;

/**
 * @author xuning
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class RsaKeyPair {
    private static final String ALGORITHM = "RSA";

    private RSAPublicKey publicKey;
    private RSAPrivateKey privateKey;

    public String getPublicKeyBase64() {
        return Base64.getEncoder().encodeToString(this.publicKey.getEncoded());
    }

    public String getPrivateKeyBase64() {
        return Base64.getEncoder().encodeToString(this.privateKey.getEncoded());
    }

    public KeyStoreProperties getKeyStoreProperties() {
        return new KeyStoreProperties().setPublicKey(getPublicKeyBase64()).setPrivateKey(getPrivateKeyBase64());
    }

    public String generateKeyStorePropertiesJson() {
        return JsonUtil.toJsonPrettify(this.getKeyStoreProperties());
    }

    @SneakyThrows
    public boolean existKeyStoreFile() {
        return new File(CONFIG_FILE_DIR_PATH + KEY_STORE_FILE_NAME).exists();
    }

    @SneakyThrows
    public RsaKeyPair autoWriteKeyStoreToConfig() {
        RsaKeyPair rsaKeyPair = randomGenerateRsaKeyPair(DEFAULT_RSA_KEY_SIZE);
        String keyStorePath = CONFIG_FILE_DIR_PATH + KEY_STORE_FILE_NAME;
        File keyStoreFile = FileUtils.getFile(keyStorePath);
        FileUtils.writeStringToFile(keyStoreFile, rsaKeyPair.generateKeyStorePropertiesJson(), Charset.defaultCharset());
        return rsaKeyPair;
    }

    @SneakyThrows
    public RsaKeyPair getConfigRsaKeyPair() {
        String path = CONFIG_FILE_DIR_PATH + KEY_STORE_FILE_NAME;
        File file = FileUtils.getFile(path);
        String json = FileUtils.readFileToString(file, Charset.defaultCharset());
        KeyStoreProperties keyStoreProperties =  JsonUtil.toBean(json, KeyStoreProperties.class);
        byte [] publicBytes = Base64.getDecoder().decode(keyStoreProperties.getPublicKey());
        RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicBytes));
        byte [] privateBytes = Base64.getDecoder().decode(keyStoreProperties.getPrivateKey());
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateBytes));
        return new RsaKeyPair().setPublicKey(rsaPublicKey).setPrivateKey(rsaPrivateKey);
    }

    @SneakyThrows
    public RsaKeyPair randomGenerateRsaKeyPair(int keySize) {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        keyPairGenerator.initialize(keySize);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        this.privateKey = (RSAPrivateKey) keyPair.getPrivate();
        this.publicKey = (RSAPublicKey) keyPair.getPublic();
        return this;
    }
}
